/*
 * Copyright (c) 2015, Freescale Semiconductor, Inc.
 * Copyright 2016-2020 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_lpi2c.h"

const LPI2C_ClockConf_Type LPI2C_ClockConf_Default =
{
    .DataValidDelayCycles = 6u,
    .SetupHoldDelayCycles = 3u,
    .ClockHighPeriodCycles = 6u,
    .ClockLowPeriodCycles = 12u
};

static void LPI2C_MasterSetClockConf(LPI2C_Type *LPI2Cx, LPI2C_ClockConf_Type *conf);

void LPI2C_MasterInit(LPI2C_Type *LPI2Cx, LPI2C_MasterConf_Type * conf, uint32_t src_clk_hz)
{
#if 1
    if (conf == NULL)
    {
        LPI2Cx->MCR = 0u; /* diable the master and reset the logic. */
        return;
    }

    /* disable the i2c master logic before any settings. */
    LPI2Cx->MCR = (conf->EnableInDebugMode ? LPI2C_MCR_DBGEN_MASK : 0u)
              | (conf->EnableInDozenMode ? LPI2C_MCR_DOZEN_MASK : 0u)
              | LPI2C_MCR_RRF_MASK | LPI2C_MCR_RTF_MASK; /* reset rx and tx fifo. */
              ;

    LPI2Cx->MCFGR0 = 0u;
    LPI2Cx->MCFGR1 = LPI2C_MCFGR1_PRESCALE(conf->ClockCycleDiv)
                 | LPI2C_MCFGR1_PINCFG(2) /* push pull mode. */
                 | (conf->EnableAutoGenSTOP ? LPI2C_MCFGR1_AUTOSTOP_MASK : 0u)
                 | (conf->EnableIgnoreAck   ? LPI2C_MCFGR1_IGNACK_MASK : 0u)
                 ;
    LPI2Cx->MCFGR2 = LPI2C_MCFGR2_BUSIDLE(conf->BusIdleTimeoutCycles);
    LPI2Cx->MFCR = LPI2C_MFCR_RXWATER(conf->RxFifoWatermark) | LPI2C_MFCR_TXWATER(conf->TxFifoWatermark);

    if (conf->ClockConf)
    {
        LPI2C_MasterSetClockConf(LPI2Cx, conf->ClockConf);
    }
    else
    {
        LPI2C_MasterSetClockConf(LPI2Cx, (LPI2C_ClockConf_Type *)&LPI2C_ClockConf_Default);
    }

    /* enable the i2c master logic. */
    LPI2Cx->MCR |= LPI2C_MCR_MEN_MASK;
#endif

#if 0
    /* Configure the baudrate. */
    LPI2Cx->MCR = 0u;
    LPI2Cx->MCCR0 = 0x0603060c;
    LPI2Cx->MCCR1 = 0x01070308; // clk & baudrate cfg

    LPI2Cx->MCFGR1 = LPI2C_MCFGR1_PINCFG(2)
                 | LPI2C_MCFGR1_PRESCALE(4)
                 | LPI2C_MCFGR1_AUTOSTOP_MASK
                 | LPI2C_MCFGR1_IGNACK_MASK;

    LPI2Cx->MFCR = LPI2C_MFCR_RXWATER(conf->RxFifoWatermark) /* Rx FIFO watermark. */
               | LPI2C_MFCR_TXWATER(conf->TxFifoWatermark);/* Tx FIFO watermark. */

    LPI2Cx->MCR = LPI2C_MCR_MEN_MASK;  /* enable master mode. */
#endif
}

static void LPI2C_MasterSetClockConf(LPI2C_Type *LPI2Cx, LPI2C_ClockConf_Type *conf)
{
    LPI2Cx->MCCR0 = LPI2C_MCCR0_DATAVD(conf->DataValidDelayCycles)
                | LPI2C_MCCR0_SETHOLD(conf->SetupHoldDelayCycles)
                | LPI2C_MCCR0_CLKHI(conf->ClockHighPeriodCycles)
                | LPI2C_MCCR0_CLKLO(conf->ClockLowPeriodCycles)
                ;
}

#if 0
void LPI2C_MasterReset(LPI2C_Type *LPI2Cx)
{
    LPI2Cx->MCR |= LPI2C_MCR_RST_MASK;
    LPI2Cx->MCR &= ~LPI2C_MCR_RST_MASK;
}

void LPI2C_MasterResetTxFifo(LPI2C_Type *LPI2Cx)
{
    LPI2Cx->MCR |= LPI2C_MCR_RTF_MASK;
}

void LPI2C_MasterResetRxFifo(LPI2C_Type *LPI2Cx)
{
    LPI2Cx->MCR |= LPI2C_MCR_RRF_MASK;
}
#endif

void LPI2C_MasterWriteTxFifoCmd(LPI2C_Type *LPI2Cx, LPI2C_TxFifoCmd_Type cmd, uint8_t data)
{
    LPI2Cx->MTDR = LPI2C_MTDR_CMD(cmd) | LPI2C_MTDR_DATA(data);
}

bool LPI2C_MasterReadRxFifoData(LPI2C_Type *LPI2Cx, uint8_t *data)
{
    uint32_t mrdr = LPI2Cx->MRDR;

    if (mrdr & LPI2C_MRDR_RXEMPTY_MASK)
    {
        return false; /* rx fifo is empty. */
    }
    *data = (uint8_t)(mrdr & LPI2C_MRDR_DATA_MASK);
    return true;
}

uint32_t LPI2C_MasterGetStatusFlags(LPI2C_Type *LPI2Cx)
{
    return LPI2Cx->MSR;
}

void LPI2C_MasterClearStatusFlags(LPI2C_Type *LPI2Cx, uint32_t flags)
{
    LPI2Cx->MSR = flags;
}

void LPI2C_MasterEnableInterrupts(LPI2C_Type *LPI2Cx, uint32_t flags)
{
    LPI2Cx->MIER |= flags;
}

void LPI2C_MasterDisableInterrupts(LPI2C_Type *LPI2Cx, uint32_t flags)
{
    LPI2Cx->MIER &= ~flags;
}

uint32_t LPI2C_MasterGetEnabledInterrupts(LPI2C_Type *LPI2Cx)
{
    return LPI2Cx->MIER;
}

void LPI2C_MasterEnableDMA(LPI2C_Type *LPI2Cx, uint32_t flags)
{
    LPI2Cx->MDER |= flags;
}

uint32_t LPI2C_MasterGetTxFifiCount(LPI2C_Type * LPI2Cx)
{
    return (LPI2Cx->MFSR & LPI2C_MFSR_TXCOUNT_MASK) >> LPI2C_MFSR_TXCOUNT_SHIFT;
}



/* for write, return the number of acks.
 * for read, return the number of received bytes.
 * return nagative number when encouting errors.
*/

//  >=0 - success; for read it's 0, for write it's number of acks received
//   <0 - error, with errno being the negative of the return value

uint32_t LPI2C_MasterWriteBlocking(LPI2C_Type *LPI2Cx, uint8_t addr, uint8_t *buf, uint32_t len, uint32_t flags)
{
    /* send start with addr byte? */
    if (0u == (flags & LPI2C_MASTER_TRANSFER_FLAG_NoStart))
    {
        while (LPI2C_MASTER_TX_FIFO_COUNT_MAX <= LPI2C_MasterGetTxFifiCount(LPI2Cx)) /* tx fifo full ? */
        {}
        LPI2C_MasterWriteTxFifoCmd(LPI2Cx, eLPI2C_TxFifoCmd_GenerateSTARTAndSendAddrByte, addr << 1u);
    }

    /* send bytes. */
    for (uint32_t i = 0u; i < len; i++)
    {
        while (LPI2C_MASTER_TX_FIFO_COUNT_MAX <= LPI2C_MasterGetTxFifiCount(LPI2Cx)) /* tx fifo full ? */
        {}
        LPI2C_MasterWriteTxFifoCmd(LPI2Cx, eLPI2C_TxFifoCmd_TxData, *buf++);
    }

    /* send stop ? */
    if (0u == (flags & LPI2C_MASTER_TRANSFER_FLAG_NoStop) )
    {
        while (LPI2C_MASTER_TX_FIFO_COUNT_MAX <= LPI2C_MasterGetTxFifiCount(LPI2Cx)) /* tx fifo full ? */
        {}
        LPI2C_MasterWriteTxFifoCmd(LPI2Cx, eLPI2C_TxFifoCmd_GenerateSTOP, 0u);
    }

    return len;
}

uint32_t LPI2C_MasterReadBlocking(LPI2C_Type *LPI2Cx, uint8_t addr, uint8_t *buf, uint32_t len, uint32_t flags)
{
    /* send start with addr byte? */
    if (0u == (flags & LPI2C_MASTER_TRANSFER_FLAG_NoStart))
    {
        while (LPI2C_MASTER_TX_FIFO_COUNT_MAX <= LPI2C_MasterGetTxFifiCount(LPI2Cx)) /* tx fifo full ? */
        {}
        LPI2C_MasterWriteTxFifoCmd(LPI2Cx, eLPI2C_TxFifoCmd_GenerateSTARTAndSendAddrByte, addr << 1u | 1u);
    }

    /* request to read bytes. */
    while (LPI2C_MASTER_TX_FIFO_COUNT_MAX <= LPI2C_MasterGetTxFifiCount(LPI2Cx)) /* tx fifo full ? */
    {}
    LPI2C_MasterWriteTxFifoCmd(LPI2Cx, eLPI2C_TxFifoCmd_RxCountOfNplus1Byte, len-1u);

    /* read bytes from fifo. */
        /* read data from rx fifo. */
    for (uint32_t i = 0u; i < len; i++)
    {
        while (!LPI2C_MasterReadRxFifoData(LPI2Cx, buf));
        buf++;
    }

    /* send stop ? */
    if (0u == (flags & LPI2C_MASTER_TRANSFER_FLAG_NoStop) )
    {
        while (LPI2C_MASTER_TX_FIFO_COUNT_MAX <= LPI2C_MasterGetTxFifiCount(LPI2Cx)) /* tx fifo full ? */
        {}
        LPI2C_MasterWriteTxFifoCmd(LPI2Cx, eLPI2C_TxFifoCmd_GenerateSTOP, 0u);
    }

    return len;
}


uint32_t LPI2C_MasterTransferBlocking(LPI2C_Type *LPI2Cx, uint8_t addr, uint8_t *buf, uint32_t len, uint32_t flags)
{
    if (flags & LPI2C_MASTER_TRANSFER_FLAG_Read)
    {
        return LPI2C_MasterReadBlocking(LPI2Cx, addr, buf, len, flags);
    }
    else
    {
        return LPI2C_MasterWriteBlocking(LPI2Cx, addr, buf, len, flags);
    }
}


uint32_t LPI2C_MasterTransferBlocking2(LPI2C_Type *LPI2Cx, uint8_t *buf, uint32_t buf_len, uint32_t flags)
{
    if (flags & LPI2C_MASTER_TRANSFER_FLAG_Read) /* read. */
    {
        if (buf_len < 1u)
        {
            return 1u; /* error: buf_len should be larger than 1 for rx. */
        }
        LPI2C_MasterWriteTxFifoCmd(LPI2Cx, eLPI2C_TxFifoCmd_RxCountOfNplus1Byte, buf_len-1u);

        /* read data from rx fifo. */
        for (uint32_t i = 0u; i < buf_len; i++)
        {
            while (!LPI2C_MasterReadRxFifoData(LPI2Cx, buf));
            buf++;
        }
    }
    else /* write. */
    {
        uint32_t buf_idx = 0u;
        /* start with target device address ? */
        if (0u == (flags & LPI2C_MASTER_TRANSFER_FLAG_NoStart) )
        {
            while (LPI2C_MASTER_TX_FIFO_COUNT_MAX <= LPI2C_MasterGetTxFifiCount(LPI2Cx)) /* tx fifo full ? */
            {}
            LPI2C_MasterWriteTxFifoCmd(LPI2Cx, eLPI2C_TxFifoCmd_GenerateSTARTAndSendAddrByte, *buf++);
            buf_idx++;
        }
        /* send tx data. */
        while (buf_idx < buf_len)
        {
            while (LPI2C_MASTER_TX_FIFO_COUNT_MAX <= LPI2C_MasterGetTxFifiCount(LPI2Cx)) /* tx fifo full ? */
            {}
            LPI2C_MasterWriteTxFifoCmd(LPI2Cx, eLPI2C_TxFifoCmd_TxData, *buf++);
            buf_idx++;
        }
    }

    /* send stop ? */
    if (0u == (flags & LPI2C_MASTER_TRANSFER_FLAG_NoStop) )
    {
        while (LPI2C_MASTER_TX_FIFO_COUNT_MAX <= LPI2C_MasterGetTxFifiCount(LPI2Cx)) /* tx fifo full ? */
        {}
        LPI2C_MasterWriteTxFifoCmd(LPI2Cx, eLPI2C_TxFifoCmd_GenerateSTOP, 0u);
    }

    return 0;
}

int32_t LPI2C_MasterWriteBlockingTimeout(LPI2C_Type *LPI2Cx, uint8_t addr, uint8_t *buf, uint32_t len, uint32_t flags, uint32_t timeout)
{
    int32_t err = 0;
    uint32_t retry = 0u;

    /* clear the flags before transfer. */
    uint32_t hw_flags = LPI2C_MasterGetStatusFlags(LPI2Cx);
    LPI2C_MasterClearStatusFlags(LPI2Cx, hw_flags);

    /* send start with addr byte? */
    if (0u == (flags & LPI2C_MASTER_TRANSFER_FLAG_NoStart))
    {
        retry = 0u;
        while (LPI2C_MASTER_TX_FIFO_COUNT_MAX <= LPI2C_MasterGetTxFifiCount(LPI2Cx)) /* tx fifo full ? */
        {
            retry++;
            if (retry > timeout)
            {
                return -1;
            }
        }
        LPI2C_MasterWriteTxFifoCmd(LPI2Cx, eLPI2C_TxFifoCmd_GenerateSTARTAndSendAddrByte, addr << 1u);
    }

    /* send bytes. */
    for (uint32_t i = 0u; i < len; i++)
    {
        retry = 0u;
        while (LPI2C_MASTER_TX_FIFO_COUNT_MAX <= LPI2C_MasterGetTxFifiCount(LPI2Cx)) /* tx fifo full ? */
        {
            retry++;
            if (retry > timeout)
            {
                return -2;
            }
        }
        LPI2C_MasterWriteTxFifoCmd(LPI2Cx, eLPI2C_TxFifoCmd_TxData, *buf++);
    }

    /* send stop ? */
    if (0u == (flags & LPI2C_MASTER_TRANSFER_FLAG_NoStop) )
    {
        retry = 0u;
        while (LPI2C_MASTER_TX_FIFO_COUNT_MAX <= LPI2C_MasterGetTxFifiCount(LPI2Cx)) /* tx fifo full ? */
        {
            retry++;
            if (retry > timeout)
            {
                return -3;
            }
        }
        LPI2C_MasterWriteTxFifoCmd(LPI2Cx, eLPI2C_TxFifoCmd_GenerateSTOP, 0u);

        /* wait for the STOP online. */
        retry = 0u;
        while (1)
        {
            hw_flags = LPI2C_MasterGetStatusFlags(LPI2Cx);
            if (LPI2C_MASTER_STATUS_StopDetect & hw_flags)
            {
                if (LPI2C_MASTER_STATUS_NackDetect & hw_flags)
                {
                    err = -5; /* stop with no ack. */
                }
                LPI2C_MasterClearStatusFlags(LPI2Cx, hw_flags);
                break;
            }
            retry++;
            if (retry > timeout)
            {
                err = -4; /* stop timeout. */
                LPI2C_MasterClearStatusFlags(LPI2Cx, hw_flags);
                break;
            }

        }
        return err;

    }

    return len;
}

int32_t LPI2C_MasterReadBlockingTimeout(LPI2C_Type *LPI2Cx, uint8_t addr, uint8_t *buf, uint32_t len, uint32_t flags, uint32_t timeout)
{
    uint32_t retry = 0u;
    /* send start with addr byte? */
    if (0u == (flags & LPI2C_MASTER_TRANSFER_FLAG_NoStart))
    {
        retry = 0u;
        while (LPI2C_MASTER_TX_FIFO_COUNT_MAX <= LPI2C_MasterGetTxFifiCount(LPI2Cx)) /* tx fifo full ? */
        {
            retry++;
            if (retry > timeout)
            {
                return -1;
            }
        }
        LPI2C_MasterWriteTxFifoCmd(LPI2Cx, eLPI2C_TxFifoCmd_GenerateSTARTAndSendAddrByte, addr << 1u | 1u);
    }

    /* request to read bytes. */
    retry = 0u;
    while (LPI2C_MASTER_TX_FIFO_COUNT_MAX <= LPI2C_MasterGetTxFifiCount(LPI2Cx)) /* tx fifo full ? */
    {
        retry++;
        if (retry > timeout)
        {
            return -2;
        }
    }
    LPI2C_MasterWriteTxFifoCmd(LPI2Cx, eLPI2C_TxFifoCmd_RxCountOfNplus1Byte, len-1u);

    /* read data from rx fifo. */
    for (uint32_t i = 0u; i < len; i++)
    {
        retry = 0u;
        while (!LPI2C_MasterReadRxFifoData(LPI2Cx, buf))
        {
            retry++;
            if (retry > timeout)
            {
                return -3;
            }
        }
        buf++;
    }

    /* send stop ? */
    if (0u == (flags & LPI2C_MASTER_TRANSFER_FLAG_NoStop) )
    {
        retry = 0u;
        while (LPI2C_MASTER_TX_FIFO_COUNT_MAX <= LPI2C_MasterGetTxFifiCount(LPI2Cx)) /* tx fifo full ? */
        {
            retry++;
            if (retry > timeout)
            {
                return -4;
            }
        }
        LPI2C_MasterWriteTxFifoCmd(LPI2Cx, eLPI2C_TxFifoCmd_GenerateSTOP, 0u);
    }

    return len;
}



/* EOF. */

